
#include "zkcfg2.h"
#include "flash.h"
#include "log/log2.h"

#define CFG_PAGE_MANU		1		//厂商配置页
#define CFG_PAGE_USER		2		//用户配置页

#define CFG_MANU_ADDR		(FLASH_BASE_ADDR + FLASH_SIZE - FLASH_PAGE_SIZE*CFG_PAGE_MANU)
#define CFG_USER_ADDR		(FLASH_BASE_ADDR + FLASH_SIZE - FLASH_PAGE_SIZE*CFG_PAGE_USER)

#define CFG_MAGIC			'Z'
#define CFG_PAGE(_addr)	(*((const volatile cfg_page_t*)_addr))

static const uint32_t cfg_page_addr[CFG_ID_MAX] = {CFG_MANU_ADDR, CFG_USER_ADDR};
static uint8_t cfg_flag_init = 0;
static uint8_t cfg_flag_sync[CFG_ID_MAX];
static cfg_page_t cfg_pages[CFG_ID_MAX];


static uint8_t cfg_block_calc_check_sum(const cfg_block_t* blk){
	const uint8_t* pdata = blk->data;
	const uint8_t* pend = pdata + sizeof(blk->data);
	uint8_t check_sum = 0;
	
	while(pdata < pend){
		check_sum += *pdata;
		pdata++;
	}
	
	return (uint8_t)(~ check_sum);
}

static int cfg_block_verify(const cfg_block_t* blk){
	if(blk->magic != CFG_MAGIC){
		return 0;
	}
	if(cfg_block_calc_check_sum(blk) != blk->check_sum){
		return 0;
	}
	return 1;
}

void cfg_init(void){
	cfg_pages[CFG_ID_MANU] = CFG_PAGE(CFG_MANU_ADDR);
	cfg_pages[CFG_ID_USER] = CFG_PAGE(CFG_USER_ADDR);
	cfg_flag_init = 0x01;
	cfg_flag_sync[0] = cfg_flag_sync[1] = 0;
}

void *cfg_get(uint8_t id, uint8_t blk){
	if(id >= CFG_ID_MAX || blk >= CFG_BLK_MAX){
		LOG2_ERR("id = %d, blk = %d", id, blk);
		return NULL;
	}

	if(!cfg_flag_init){
		cfg_init();
	}
	cfg_block_t* block = &cfg_pages[id].blocks[blk];
	if(cfg_block_verify(block)){
		return block;
	}
	return NULL;
}

int cfg_update(uint8_t id, uint8_t blk, const void* data, uint8_t len){
	cfg_block_t* block = &cfg_pages[id].blocks[blk];
	if(id >= CFG_ID_MAX || blk >= CFG_BLK_MAX || !data || len > sizeof(block->data)){
		LOG2_ERR("id = %d, blk = %d, data = %p, len = %d", id, blk, data, len);
		return -1;
	}
	if(!cfg_flag_init){
		cfg_init();
	}
	cfg_flag_sync[id] = 1;
	memcpy(block->data, data, len);
	block->check_sum = cfg_block_calc_check_sum(block);
	block->magic = CFG_MAGIC;
	return 0;
}

static int cfg_flush_page(uint32_t addr, cfg_page_t* page){
	flash_erase(addr, 1);
	uint32_t* data = (uint32_t*)page;
	const uint32_t dw_count = sizeof(*page) / sizeof(uint32_t);
	return flash_write(addr, data, dw_count);
}

int cfg_flush(void){
	if(!cfg_flag_init){
		return 0;
	}
	
	int ret = 0;
	
	for(int i = 0; i < CFG_ID_MAX; i++){
		if(cfg_flag_sync[i]){
			ret |= cfg_flush_page(cfg_page_addr[i], &cfg_pages[i]);
		}
	}

	return ret;
}

